/**
 *
 * @Author: Marek Zakowicz <mazak (at) debian (dot) linux.org.pl>
 *
 * Copyright (c) 2005, 2006, 2007 Marek Zakowicz. All rights reserved.
 *
 * Licence: This code may be used, (re)distribute and/or modify under the terms one of the following two kinds of licence:
 *          1) as free software under GPL 3 (General Public Licence ver. 3 as published by the Free Software Foundation)
 *          2) as software under the terms individual licence from Marek Zakowicz
 *
 *          This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *          without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

package jsp2php;

import java.util.*;

class StaticStringTranslator extends TranslatorBase {

   static {
      AddOns.add("j2p_valueOf", "function j2p_valueOf($str) { return is_null($str) ? \"null\" : $str; }");
   }

   public String name() {
      return "java.lang.String";
   }

   public String php_name() {
      return "string";
   }

   // STATIC TRANSLATOR

   public Translation translate_valueOf(Translation ... args) throws TranslatorException {
      validate_arg_no(1, "valueOf", args);

      Translation arg = args[1];
      if( arg.type == JavaType.bool() ) {
         String s;
         if( arg.consistent ) {
            s = arg.text +" ? \"true\" : \"false\"";
         }
         else {
            s = "("+ arg.text +") ? \"true\" : \"false\"";
         }
         return new Translation(s, JavaType.string(), false, arg.not_null);
      }
      else if( arg.type == JavaType.integer() || arg.type == JavaType._double() ) {
         return new Translation("(string) " + arg.paren(), JavaType.string(), false, arg.not_null);
      }
      else if( arg.type == JavaType.string() ) {
         AddOns.use("j2p_valueOf");
         return new Translation("j2p_valueOf("+ arg.text +")", JavaType.string(), true, true);
      }
      throw new TranslatorException("Unsupported static method "+ name() +".valueOf("+ arg.type.name() +")!");
   }
}

/**
 * @composed 1 - 1 StringValueTranslator
 */
class StringTranslator extends StaticStringTranslator implements ITypeTranslator {

   private static StringValueTranslator value_translator = new StringValueTranslator();

   static {
      ITypeTranslator tr = new StringTranslator();
      JavaType.add_type_translator( tr );
   }

   private StringTranslator() {}

   public IValueTranslator value_translator() {
      return value_translator;
   }
}

class StringValueTranslator extends StaticStringTranslator implements IValueTranslator {

   static {
      AddOns.add("j2p_endsWith", "function j2p_endsWith($str, $suffix) { return substr($str, -strlen($suffix)) == $suffix; }");
      AddOns.add("j2p_indexOf",  "function j2p_indexOf($str1, $str2) { return '' === $str2 ? 0 : ( strpos($str1, $str2) === false ? -1 : strpos($str1, $str2) ); }");
      AddOns.add("j2p_substring", "function j2p_substring($str, $beginIndex, $endIndex) { return (string) substr($str, $beginIndex, $endIndex - $beginIndex); }");
   }

   public Translation literal(String value) throws TranslatorException {
      // TODO: formatowanie znakow specjalnych, przydalby sie par. konf. okreslajacy kodowanie w PHP np. iso-8859-2 - wtedy mozna przekodowac napis, lub zwrocic wyjatek.
      return new Translation(value, JavaType.string(), true, true);
   }

   public Translation add(Translation left, Translation right) throws TranslatorException {
      String left_value, right_value;

      if( left.type == JavaType.bool() ) {
         left_value = "( "+ left.text +" ? \"true\" : \"false\" )";
      }
      else {
         left_value = left.text;
      }

      if( right.type == JavaType.bool() ) {
         right_value = "( "+ right.text +" ? \"true\" : \"false\" )";
      }
      else {
         right_value = right.text;
      }

      return new Translation(left_value + " . " + right_value, JavaType.string(), false, left.not_null || right.not_null); // String addition result is not null, if one of args is not null!
   }

   public Translation add_eq(Translation left, Translation right) throws TranslatorException {
      String right_value;

      if( right.type == JavaType.bool() ) {
         right_value = "( "+ right.text +" ? \"true\" : \"false\" )";
      }
      else {
         right_value = right.text;
      }

      // String addition result is not null, if one of args is not null!

      String left_text = left.text;
      if( right.modifiedVariables.contains(left_text) ) {
         return new Translation(left_text + " = " + left_text +" . "+ right_value, JavaType.string(), false, left.not_null || right.not_null, right.modifiedVariables);
      }
      else {
         Set<String> modifiedVariables = new HashSet<String>( right.modifiedVariables );
         modifiedVariables.add(left_text);
         return new Translation(left_text + " .= " + right_value, JavaType.string(), false, left.not_null || right.not_null, modifiedVariables);
      }
   }

   public Translation translate_compareTo(Translation ... args) throws TranslatorException {
      // strcmp nigdy nie jest null
      return translate("compareTo", "strcmp(%1$s, %2$s)", 1, JavaType.integer(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args);
   }

   public Translation translate_compareToIgnoreCase(Translation ... args) throws TranslatorException {
      // strcmp nigdy nie jest null
      return translate("compareToIgnoreCase", "strcasecmp(%1$s, %2$s)", 1, JavaType.integer(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args);
   }

   public Translation translate_concat(Translation ... args) throws TranslatorException {
      // strcmp nigdy nie jest null
      return translate("concat", "%1$s . %2$s", 1, JavaType.integer(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args, false);
   }

   public Translation translate_endsWith(Translation ... args) throws TranslatorException {
      AddOns.use("j2p_endsWith"); // TODO: migracja do j2p_ends_with
      return translate("endsWith", "j2p_endsWith(%1$s, %2$s)", 1, JavaType.bool(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args);
   }

   public Translation translate_equals(Translation ... args) throws TranslatorException {
      // === nigdy nie jest null
      return translate("equals", "%1$s === %2$s", 1, JavaType.bool(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args, false);
   }

   public Translation translate_equalsIgnoreCase(Translation ... args) throws TranslatorException {
      // === nigdy nie jest null
      return translate("equalsIgnoreCase", "strcasecmp(%1$s, %2$s) == 0", 1, JavaType.bool(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args, false);
   }

   public Translation translate_hashCode(Translation ... args) throws TranslatorException {
      return translate("hashCode", "crc32(%s)", JavaType.string(), true, args);
   }

   public Translation translate_indexOf(Translation ... args) throws TranslatorException {
      AddOns.use("j2p_indexOf"); // TODO: migracja do j2p_index_of
      return translate("indexOf", "j2p_indexOf(%1$s, %2$s)", 1, JavaType.integer(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args);
   }

   public Translation translate_intern(Translation ... args) throws TranslatorException {
      validate_arg_no(0, "intern", args);
      return args[0];
   }

   public Translation translate_length(Translation ... args) throws TranslatorException {
      return translate("length", "strlen(%s)", JavaType.integer(), true, args); // strlen nigdy nie jest null
   }

   public Translation translate_matches(Translation ... args) throws TranslatorException {
      return translate("matches", "(bool) ereg(%2$s, %1$s)", 1, JavaType.bool(), new JavaType[]{JavaType.string()}, true, args, false);
   }

   public Translation translate_replaceAll(Translation ... args) throws TranslatorException {
      return translate("replaceAll", "ereg_replace(%2$s, %3$s, %1$s)", 2, JavaType.string(), new JavaType[]{JavaType.string(), JavaType.string()}, true, args);
      // jesli wyrazenie reg. jest puste replaceAll rzuca wyjatek, a ereg_replace zwraca false
   }

   public Translation translate_startsWith(Translation ... args) throws TranslatorException {
      validate_arg_no(new int[]{1,2}, "startsWith", args);
      if(args.length == 2) {
         return translate("startsWith", "strpos(%1$s, %2$s) === 0", 1, JavaType.bool(), new JavaType[]{JavaType.string()}, true, args, false);
      }
      // TODO: Ponizsze powinno byc w funkcji, bo jest powtarzane wyrazenie...
      return translate("startsWith", "strpos(%1$s, %2$s, %3$s) === %3$s", 2, JavaType.bool(), new JavaType[]{JavaType.string(), JavaType.integer()}, true, args, false);
      //return translate("startsWith", "strpos(%1$s, %2$s, %3$s) === 0", 2, JavaType.bool(), new JavaType[]{JavaType.string(), JavaType.string(), JavaType.integer()}, true, args, false);
   }

   public Translation translate_substring(Translation ... args) throws TranslatorException {
      validate_arg_no(new int[]{1,2}, "startsWith", args);
      if(args.length==2) {
         return translate("substring", "(string) substr(%1$s, %2$s)", 1, JavaType.string(), new JavaType[]{JavaType.integer()}, true, args, false);
      }
      AddOns.use("j2p_substring");
      return translate("substring", "j2p_substring(%1$s, %2$s, %3$s)", 2, JavaType.string(), new JavaType[]{JavaType.integer(), JavaType.integer()}, true, args);
   }

   public Translation translate_toLowerCase(Translation ... args) throws TranslatorException {
      return translate("toLowerCase", "strtolower(%s)", JavaType.string(), true, args); // strtolower(null) === ""
   }

   public Translation translate_toString(Translation ... args) throws TranslatorException {
      validate_arg_no(0, "toString", args);
      return args[0];
   }

   public Translation translate_toUpperCase(Translation ... args) throws TranslatorException {
      return translate("toUpperCase", "strtoupper(%s)", JavaType.string(), true, args); // strtoupper(null) === ""
   }

   public Translation translate_trim(Translation ... args) throws TranslatorException {
      return translate("trim", "trim(%s, \"\\x00..\\x20\")", JavaType.string(), true, args); // trim(null) === ""
   }

}
